Code Coverage
 
Classes and Traits
Functions and Methods
Lines
Total
0.00% covered (danger)
0.00%
0 / 1
54.55% covered (warning)
54.55%
12 / 22
CRAP
95.98% covered (success)
95.98%
1074 / 1119
PptCharts
0.00% covered (danger)
0.00%
0 / 1
54.55% covered (warning)
54.55%
12 / 22
198
95.98% covered (success)
95.98%
1074 / 1119
 render
0.00% covered (danger)
0.00%
0 / 1
5.02
90.91% covered (success)
90.91%
10 / 11
 writeChart
0.00% covered (danger)
0.00%
0 / 1
8.23
84.72% covered (warning)
84.72%
61 / 72
 writeSpreadsheet
0.00% covered (danger)
0.00%
0 / 1
7.04
90.32% covered (success)
90.32%
28 / 31
 writeElementWithValAttribute
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
 writeSingleValueOrReference
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
16 / 16
 writeMultipleValuesOrReference
100.00% covered (success)
100.00%
1 / 1
6
100.00% covered (success)
100.00%
26 / 26
 writeTitle
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
48 / 48
 writePlotArea
100.00% covered (success)
100.00%
1 / 1
11
100.00% covered (success)
100.00%
26 / 26
 writeLegend
100.00% covered (success)
100.00%
1 / 1
5
100.00% covered (success)
100.00%
48 / 48
 writeLayout
0.00% covered (danger)
0.00%
0 / 1
5.27
77.78% covered (warning)
77.78%
21 / 27
 writeTypeArea
0.00% covered (danger)
0.00%
0 / 1
9.02
93.48% covered (success)
93.48%
43 / 46
 writeTypeBar
0.00% covered (danger)
0.00%
0 / 1
20.25
91.43% covered (success)
91.43%
96 / 105
 writeTypeBar3D
0.00% covered (danger)
0.00%
0 / 1
15
96.74% covered (success)
96.74%
89 / 92
 writeTypeDoughnut
0.00% covered (danger)
0.00%
0 / 1
20
95.29% covered (success)
95.29%
81 / 85
 writeTypePie
0.00% covered (danger)
0.00%
0 / 1
15.03
94.94% covered (success)
94.94%
75 / 79
 writeTypePie3D
100.00% covered (success)
100.00%
1 / 1
13
100.00% covered (success)
100.00%
76 / 76
 writeTypeLine
100.00% covered (success)
100.00%
1 / 1
12
100.00% covered (success)
100.00%
81 / 81
 writeTypeScatter
100.00% covered (success)
100.00%
1 / 1
15
100.00% covered (success)
100.00%
85 / 85
 writeChartRelationships
100.00% covered (success)
100.00%
1 / 1
2
100.00% covered (success)
100.00%
8 / 8
 writeSeriesMarker
100.00% covered (success)
100.00%
1 / 1
4
100.00% covered (success)
100.00%
19 / 19
 writeAxis
0.00% covered (danger)
0.00%
0 / 1
18
99.23% covered (success)
99.23%
129 / 130
 writeAxisGridlines
100.00% covered (success)
100.00%
1 / 1
1
100.00% covered (success)
100.00%
4 / 4
1<?php
2
3namespace PhpOffice\PhpPresentation\Writer\PowerPoint2007;
4
5use PhpOffice\Common\Adapter\Zip\ZipInterface;
6use PhpOffice\Common\Drawing as CommonDrawing;
7use PhpOffice\Common\XMLWriter;
8use PhpOffice\PhpPresentation\PhpPresentation;
9use PhpOffice\PhpPresentation\Shape\Chart;
10use PhpOffice\PhpPresentation\Shape\Chart\Gridlines;
11use PhpOffice\PhpPresentation\Shape\Chart\Legend;
12use PhpOffice\PhpPresentation\Shape\Chart\PlotArea;
13use PhpOffice\PhpPresentation\Shape\Chart\Title;
14use PhpOffice\PhpPresentation\Shape\Chart\Type\Area;
15use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar;
16use PhpOffice\PhpPresentation\Shape\Chart\Type\Bar3D;
17use PhpOffice\PhpPresentation\Shape\Chart\Type\Doughnut;
18use PhpOffice\PhpPresentation\Shape\Chart\Type\Line;
19use PhpOffice\PhpPresentation\Shape\Chart\Type\Pie;
20use PhpOffice\PhpPresentation\Shape\Chart\Type\Pie3D;
21use PhpOffice\PhpPresentation\Shape\Chart\Type\Scatter;
22use PhpOffice\PhpPresentation\Style\Border;
23use PhpOffice\PhpPresentation\Style\Fill;
24use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
25use PhpOffice\PhpSpreadsheet\IOFactory;
26use PhpOffice\PhpSpreadsheet\Spreadsheet;
27
28class PptCharts extends AbstractDecoratorWriter
29{
30    /**
31     * @throws \Exception
32     */
33    public function render(): ZipInterface
34    {
35        for ($i = 0; $i < $this->getDrawingHashTable()->count(); ++$i) {
36            $shape = $this->getDrawingHashTable()->getByIndex($i);
37            if ($shape instanceof Chart) {
38                $this->getZip()->addFromString('ppt/charts/' . $shape->getIndexedFilename(), $this->writeChart($shape));
39
40                if ($shape->hasIncludedSpreadsheet()) {
41                    $this->getZip()->addFromString('ppt/charts/_rels/' . $shape->getIndexedFilename() . '.rels', $this->writeChartRelationships($shape));
42                    $pFilename = tempnam(sys_get_temp_dir(), 'PhpSpreadsheet');
43                    $this->getZip()->addFromString('ppt/embeddings/' . $shape->getIndexedFilename() . '.xlsx', $this->writeSpreadsheet($this->getPresentation(), $shape, $pFilename . '.xlsx'));
44
45                    // remove temp file
46                    if (false === @unlink($pFilename)) {
47                        throw new \Exception('The file ' . $pFilename . ' could not removed.');
48                    }
49                }
50            }
51        }
52
53        return $this->getZip();
54    }
55
56    /**
57     * Write chart to XML format.
58     *
59     * @return string XML Output
60     *
61     * @throws \Exception
62     */
63    public function writeChart(Chart $chart): string
64    {
65        // Create XML writer
66        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
67
68        // XML header
69        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
70
71        // c:chartSpace
72        $objWriter->startElement('c:chartSpace');
73        $objWriter->writeAttribute('xmlns:c', 'http://schemas.openxmlformats.org/drawingml/2006/chart');
74        $objWriter->writeAttribute('xmlns:a', 'http://schemas.openxmlformats.org/drawingml/2006/main');
75        $objWriter->writeAttribute('xmlns:r', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships');
76
77        // c:date1904
78        $objWriter->startElement('c:date1904');
79        $objWriter->writeAttribute('val', '1');
80        $objWriter->endElement();
81
82        // c:lang
83        $objWriter->startElement('c:lang');
84        $objWriter->writeAttribute('val', 'en-US');
85        $objWriter->endElement();
86
87        // c:chart
88        $objWriter->startElement('c:chart');
89
90        // Title?
91        if ($chart->getTitle()->isVisible()) {
92            // Write title
93            $this->writeTitle($objWriter, $chart->getTitle());
94        }
95
96        // c:autoTitleDeleted
97        $objWriter->startElement('c:autoTitleDeleted');
98        $objWriter->writeAttribute('val', $chart->getTitle()->isVisible() ? '0' : '1');
99        $objWriter->endElement();
100
101        // c:view3D
102        $objWriter->startElement('c:view3D');
103
104        // c:rotX
105        $objWriter->startElement('c:rotX');
106        $objWriter->writeAttribute('val', $chart->getView3D()->getRotationX());
107        $objWriter->endElement();
108
109        // c:hPercent
110        $hPercent = $chart->getView3D()->getHeightPercent();
111        $objWriter->writeElementIf(null != $hPercent, 'c:hPercent', 'val', $hPercent);
112
113        // c:rotY
114        $objWriter->startElement('c:rotY');
115        $objWriter->writeAttribute('val', $chart->getView3D()->getRotationY());
116        $objWriter->endElement();
117
118        // c:depthPercent
119        $objWriter->startElement('c:depthPercent');
120        $objWriter->writeAttribute('val', $chart->getView3D()->getDepthPercent());
121        $objWriter->endElement();
122
123        // c:rAngAx
124        $objWriter->startElement('c:rAngAx');
125        $objWriter->writeAttribute('val', $chart->getView3D()->hasRightAngleAxes() ? '1' : '0');
126        $objWriter->endElement();
127
128        // c:perspective
129        $objWriter->startElement('c:perspective');
130        $objWriter->writeAttribute('val', $chart->getView3D()->getPerspective());
131        $objWriter->endElement();
132
133        $objWriter->endElement();
134
135        // Write plot area
136        $this->writePlotArea($objWriter, $chart->getPlotArea(), $chart);
137
138        // Legend?
139        if ($chart->getLegend()->isVisible()) {
140            // Write legend
141            $this->writeLegend($objWriter, $chart->getLegend());
142        }
143
144        // c:plotVisOnly
145        $objWriter->startElement('c:plotVisOnly');
146        $objWriter->writeAttribute('val', '1');
147        $objWriter->endElement();
148
149        // c:dispBlanksAs
150        $objWriter->startElement('c:dispBlanksAs');
151        $objWriter->writeAttribute('val', $chart->getDisplayBlankAs());
152        $objWriter->endElement();
153
154        $objWriter->endElement();
155
156        // c:spPr
157        $objWriter->startElement('c:spPr');
158
159        // Fill
160        $this->writeFill($objWriter, $chart->getFill());
161
162        // Border
163        if (Border::LINE_NONE != $chart->getBorder()->getLineStyle()) {
164            $this->writeBorder($objWriter, $chart->getBorder(), '');
165        }
166
167        // Shadow
168        if ($chart->getShadow()->isVisible()) {
169            // a:effectLst
170            $objWriter->startElement('a:effectLst');
171
172            // a:outerShdw
173            $objWriter->startElement('a:outerShdw');
174            $objWriter->writeAttribute('blurRad', CommonDrawing::pixelsToEmu($chart->getShadow()->getBlurRadius()));
175            $objWriter->writeAttribute('dist', CommonDrawing::pixelsToEmu($chart->getShadow()->getDistance()));
176            $objWriter->writeAttribute('dir', CommonDrawing::degreesToAngle($chart->getShadow()->getDirection()));
177            $objWriter->writeAttribute('algn', $chart->getShadow()->getAlignment());
178            $objWriter->writeAttribute('rotWithShape', '0');
179
180            $this->writeColor($objWriter, $chart->getShadow()->getColor(), $chart->getShadow()->getAlpha());
181
182            $objWriter->endElement();
183
184            $objWriter->endElement();
185        }
186
187        $objWriter->endElement();
188
189        // External data?
190        if ($chart->hasIncludedSpreadsheet()) {
191            // c:externalData
192            $objWriter->startElement('c:externalData');
193            $objWriter->writeAttribute('r:id', 'rId1');
194
195            // c:autoUpdate
196            $objWriter->startElement('c:autoUpdate');
197            $objWriter->writeAttribute('val', '0');
198            $objWriter->endElement();
199
200            $objWriter->endElement();
201        }
202
203        $objWriter->endElement();
204
205        // Return
206        return $objWriter->getData();
207    }
208
209    /**
210     * Write chart to XML format.
211     *
212     * @return string String output
213     *
214     * @throws \Exception
215     */
216    public function writeSpreadsheet(PhpPresentation $presentation, Chart $chart, string $tempName): string
217    {
218        // Need output?
219        if (!$chart->hasIncludedSpreadsheet()) {
220            throw new \Exception('No spreadsheet output is required for the given chart.');
221        }
222
223        // Create new spreadsheet
224        $spreadsheet = new Spreadsheet();
225
226        // Set properties
227        $title = $chart->getTitle()->getText();
228        if (0 == strlen($title)) {
229            $title = 'Chart';
230        }
231        $spreadsheet->getProperties()
232            ->setCreator(
233                $presentation->getDocumentProperties()->getCreator())->setLastModifiedBy(
234                    $presentation->getDocumentProperties()->getLastModifiedBy()
235                )
236            ->setTitle($title);
237
238        // Add chart data
239        $sheet = $spreadsheet->setActiveSheetIndex(0);
240        $sheet->setTitle('Sheet1');
241
242        // Write series
243        $seriesIndex = 0;
244        foreach ($chart->getPlotArea()->getType()->getSeries() as $series) {
245            // Title
246            $sheet->setCellValueByColumnAndRow(1 + $seriesIndex, 1, $series->getTitle());
247
248            // X-axis
249            $axisXData = array_keys($series->getValues());
250            $numAxisXData = count($axisXData);
251            for ($i = 0; $i < $numAxisXData; ++$i) {
252                $sheet->setCellValueByColumnAndRow(0, $i + 2, $axisXData[$i]);
253            }
254
255            // Y-axis
256            $axisYData = array_values($series->getValues());
257            $numAxisYData = count($axisYData);
258            for ($i = 0; $i < $numAxisYData; ++$i) {
259                $sheet->setCellValueByColumnAndRow(1 + $seriesIndex, $i + 2, $axisYData[$i]);
260            }
261
262            ++$seriesIndex;
263        }
264
265        // Save to string
266        $writer = IOFactory::createWriter($spreadsheet, 'Xlsx');
267        $writer->save($tempName);
268
269        // Load file in memory
270        $returnValue = file_get_contents($tempName);
271        if (false === @unlink($tempName)) {
272            throw new \Exception('The file ' . $tempName . ' could not removed.');
273        }
274
275        return $returnValue;
276    }
277
278    /**
279     * Write element with value attribute.
280     *
281     * @param XMLWriter $objWriter XML Writer
282     */
283    protected function writeElementWithValAttribute(XMLWriter $objWriter, string $elementName, string $value): void
284    {
285        $objWriter->startElement($elementName);
286        $objWriter->writeAttribute('val', $value);
287        $objWriter->endElement();
288    }
289
290    /**
291     * Write single value or reference.
292     *
293     * @param XMLWriter $objWriter XML Writer
294     */
295    protected function writeSingleValueOrReference(XMLWriter $objWriter, bool $isReference, string $value, string $reference): void
296    {
297        if (!$isReference) {
298            // Value
299            $objWriter->writeElement('c:v', $value);
300
301            return;
302        }
303
304        // Reference and cache
305        // c:strRef
306        $objWriter->startElement('c:strRef');
307        // c:strRef/c:f
308        $objWriter->writeElement('c:f', $reference);
309        // c:strRef/c:strCache
310        $objWriter->startElement('c:strCache');
311        // c:strRef/c:strCache/c:ptCount
312        $objWriter->startElement('c:ptCount');
313        $objWriter->writeAttribute('val', '1');
314        $objWriter->endElement();
315
316        // c:strRef/c:strCache/c:pt
317        $objWriter->startElement('c:pt');
318        $objWriter->writeAttribute('idx', '0');
319        // c:strRef/c:strCache/c:pt/c:v
320        $objWriter->writeElement('c:v', $value);
321        // c:strRef/c:strCache/c:pt
322        $objWriter->endElement();
323        // c:strRef/c:strCache
324        $objWriter->endElement();
325        // c:strRef
326        $objWriter->endElement();
327    }
328
329    /**
330     * Write series value or reference.
331     *
332     * @param XMLWriter $objWriter XML Writer
333     * @param array<int, mixed> $values
334     */
335    protected function writeMultipleValuesOrReference(XMLWriter $objWriter, bool $isReference, array $values, string $reference): void
336    {
337        // c:strLit / c:numLit
338        // c:strRef / c:numRef
339        $referenceType = ($isReference ? 'Ref' : 'Lit');
340        $dataType = is_numeric($values[0]) ? 'num' : 'str';
341        $objWriter->startElement('c:' . $dataType . $referenceType);
342
343        $numValues = count($values);
344        if (!$isReference) {
345            // Value
346
347            // c:ptCount
348            $objWriter->startElement('c:ptCount');
349            $objWriter->writeAttribute('val', count($values));
350            $objWriter->endElement();
351
352            // Add points
353            for ($i = 0; $i < $numValues; ++$i) {
354                // c:pt
355                $objWriter->startElement('c:pt');
356                $objWriter->writeAttribute('idx', $i);
357                $objWriter->writeElement('c:v', $values[$i]);
358                $objWriter->endElement();
359            }
360        } else {
361            // Reference
362            $objWriter->writeElement('c:f', $reference);
363            $objWriter->startElement('c:' . $dataType . 'Cache');
364
365            // c:ptCount
366            $objWriter->startElement('c:ptCount');
367            $objWriter->writeAttribute('val', count($values));
368            $objWriter->endElement();
369
370            // Add points
371            for ($i = 0; $i < $numValues; ++$i) {
372                // c:pt
373                $objWriter->startElement('c:pt');
374                $objWriter->writeAttribute('idx', $i);
375                $objWriter->writeElement('c:v', $values[$i]);
376                $objWriter->endElement();
377            }
378
379            $objWriter->endElement();
380        }
381
382        $objWriter->endElement();
383    }
384
385    /**
386     * Write Title.
387     *
388     * @param XMLWriter $objWriter XML Writer
389     *
390     * @throws \Exception
391     */
392    protected function writeTitle(XMLWriter $objWriter, Title $subject): void
393    {
394        // c:title
395        $objWriter->startElement('c:title');
396
397        // c:tx
398        $objWriter->startElement('c:tx');
399
400        // c:rich
401        $objWriter->startElement('c:rich');
402
403        // a:bodyPr
404        $objWriter->writeElement('a:bodyPr', null);
405
406        // a:lstStyle
407        $objWriter->writeElement('a:lstStyle', null);
408
409        // a:p
410        $objWriter->startElement('a:p');
411
412        // a:pPr
413        $objWriter->startElement('a:pPr');
414        $objWriter->writeAttribute('algn', $subject->getAlignment()->getHorizontal());
415        $objWriter->writeAttribute('fontAlgn', $subject->getAlignment()->getVertical());
416        $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginLeft()));
417        $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginRight()));
418        $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($subject->getAlignment()->getIndent()));
419        $objWriter->writeAttribute('lvl', $subject->getAlignment()->getLevel());
420
421        // a:defRPr
422        $objWriter->writeElement('a:defRPr', null);
423
424        $objWriter->endElement();
425
426        // a:r
427        $objWriter->startElement('a:r');
428
429        // a:rPr
430        $objWriter->startElement('a:rPr');
431        $objWriter->writeAttribute('lang', 'en-US');
432        $objWriter->writeAttribute('dirty', '0');
433        $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false'));
434        $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false'));
435        $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
436        $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100));
437        $objWriter->writeAttribute('u', $subject->getFont()->getUnderline());
438        $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000');
439        $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000');
440
441        // Font - a:solidFill
442        $objWriter->startElement('a:solidFill');
443
444        $this->writeColor($objWriter, $subject->getFont()->getColor());
445
446        $objWriter->endElement();
447
448        // Font - a:latin
449        $objWriter->startElement('a:latin');
450        $objWriter->writeAttribute('typeface', $subject->getFont()->getName());
451        $objWriter->endElement();
452
453        $objWriter->endElement();
454
455        // a:t
456        $objWriter->writeElement('a:t', $subject->getText());
457
458        $objWriter->endElement();
459
460        // a:endParaRPr
461        $objWriter->startElement('a:endParaRPr');
462        $objWriter->writeAttribute('lang', 'en-US');
463        $objWriter->writeAttribute('dirty', '0');
464        $objWriter->endElement();
465
466        $objWriter->endElement();
467
468        $objWriter->endElement();
469
470        $objWriter->endElement();
471
472        // Write layout
473        $this->writeLayout($objWriter, $subject);
474
475        // c:overlay
476        $objWriter->startElement('c:overlay');
477        $objWriter->writeAttribute('val', '0');
478        $objWriter->endElement();
479
480        $objWriter->endElement();
481    }
482
483    /**
484     * Write Plot Area.
485     *
486     * @param XMLWriter $objWriter XML Writer
487     *
488     * @throws \Exception
489     */
490    protected function writePlotArea(XMLWriter $objWriter, PlotArea $subject, Chart $chart): void
491    {
492        // c:plotArea
493        $objWriter->startElement('c:plotArea');
494
495        // Write layout
496        $this->writeLayout($objWriter, $subject);
497
498        // Write chart
499        $chartType = $subject->getType();
500        if ($chartType instanceof Area) {
501            $this->writeTypeArea($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
502        } elseif ($chartType instanceof Bar) {
503            $this->writeTypeBar($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
504        } elseif ($chartType instanceof Bar3D) {
505            $this->writeTypeBar3D($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
506        } elseif ($chartType instanceof Doughnut) {
507            $this->writeTypeDoughnut($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
508        } elseif ($chartType instanceof Pie) {
509            $this->writeTypePie($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
510        } elseif ($chartType instanceof Pie3D) {
511            $this->writeTypePie3D($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
512        } elseif ($chartType instanceof Line) {
513            $this->writeTypeLine($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
514        } elseif ($chartType instanceof Scatter) {
515            $this->writeTypeScatter($objWriter, $chartType, $chart->hasIncludedSpreadsheet());
516        } else {
517            throw new \Exception('The chart type provided could not be rendered.');
518        }
519
520        // Write X axis?
521        if ($chartType->hasAxisX()) {
522            $this->writeAxis($objWriter, $subject->getAxisX(), Chart\Axis::AXIS_X, $chartType);
523        }
524
525        // Write Y axis?
526        if ($chartType->hasAxisY()) {
527            $this->writeAxis($objWriter, $subject->getAxisY(), Chart\Axis::AXIS_Y, $chartType);
528        }
529
530        $objWriter->endElement();
531    }
532
533    /**
534     * Write Legend.
535     *
536     * @param XMLWriter $objWriter XML Writer
537     * @param Chart\Legend $subject
538     *
539     * @throws \Exception
540     */
541    protected function writeLegend(XMLWriter $objWriter, Legend $subject): void
542    {
543        // c:legend
544        $objWriter->startElement('c:legend');
545
546        // c:legendPos
547        $objWriter->startElement('c:legendPos');
548        $objWriter->writeAttribute('val', $subject->getPosition());
549        $objWriter->endElement();
550
551        // Write layout
552        $this->writeLayout($objWriter, $subject);
553
554        // c:overlay
555        $objWriter->startElement('c:overlay');
556        $objWriter->writeAttribute('val', '0');
557        $objWriter->endElement();
558
559        // c:spPr
560        $objWriter->startElement('c:spPr');
561
562        // Fill
563        $this->writeFill($objWriter, $subject->getFill());
564
565        // Border
566        if (Border::LINE_NONE != $subject->getBorder()->getLineStyle()) {
567            $this->writeBorder($objWriter, $subject->getBorder(), '');
568        }
569
570        $objWriter->endElement();
571
572        // c:txPr
573        $objWriter->startElement('c:txPr');
574
575        // a:bodyPr
576        $objWriter->writeElement('a:bodyPr', null);
577
578        // a:lstStyle
579        $objWriter->writeElement('a:lstStyle', null);
580
581        // a:p
582        $objWriter->startElement('a:p');
583
584        // a:pPr
585        $objWriter->startElement('a:pPr');
586        $objWriter->writeAttribute('algn', $subject->getAlignment()->getHorizontal());
587        $objWriter->writeAttribute('fontAlgn', $subject->getAlignment()->getVertical());
588        $objWriter->writeAttribute('marL', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginLeft()));
589        $objWriter->writeAttribute('marR', CommonDrawing::pixelsToEmu($subject->getAlignment()->getMarginRight()));
590        $objWriter->writeAttribute('indent', CommonDrawing::pixelsToEmu($subject->getAlignment()->getIndent()));
591        $objWriter->writeAttribute('lvl', $subject->getAlignment()->getLevel());
592
593        // a:defRPr
594        $objWriter->startElement('a:defRPr');
595
596        $objWriter->writeAttribute('b', ($subject->getFont()->isBold() ? 'true' : 'false'));
597        $objWriter->writeAttribute('i', ($subject->getFont()->isItalic() ? 'true' : 'false'));
598        $objWriter->writeAttribute('strike', ($subject->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
599        $objWriter->writeAttribute('sz', ($subject->getFont()->getSize() * 100));
600        $objWriter->writeAttribute('u', $subject->getFont()->getUnderline());
601        $objWriter->writeAttributeIf($subject->getFont()->isSuperScript(), 'baseline', '300000');
602        $objWriter->writeAttributeIf($subject->getFont()->isSubScript(), 'baseline', '-250000');
603
604        // Font - a:solidFill
605        $objWriter->startElement('a:solidFill');
606
607        $this->writeColor($objWriter, $subject->getFont()->getColor());
608
609        $objWriter->endElement();
610
611        // Font - a:latin
612        $objWriter->startElement('a:latin');
613        $objWriter->writeAttribute('typeface', $subject->getFont()->getName());
614        $objWriter->endElement();
615
616        $objWriter->endElement();
617
618        $objWriter->endElement();
619
620        // a:endParaRPr
621        $objWriter->startElement('a:endParaRPr');
622        $objWriter->writeAttribute('lang', 'en-US');
623        $objWriter->writeAttribute('dirty', '0');
624        $objWriter->endElement();
625
626        $objWriter->endElement();
627
628        $objWriter->endElement();
629
630        $objWriter->endElement();
631    }
632
633    /**
634     * Write Layout.
635     *
636     * @param XMLWriter $objWriter XML Writer
637     * @param Legend|PlotArea|Title $subject
638     *
639     * @throws \Exception
640     */
641    protected function writeLayout(XMLWriter $objWriter, $subject): void
642    {
643        // c:layout
644        $objWriter->startElement('c:layout');
645
646        // c:manualLayout
647        $objWriter->startElement('c:manualLayout');
648        // c:xMode
649        $objWriter->startElement('c:xMode');
650        $objWriter->writeAttribute('val', 'edge');
651        $objWriter->endElement();
652
653        // c:yMode
654        $objWriter->startElement('c:yMode');
655        $objWriter->writeAttribute('val', 'edge');
656        $objWriter->endElement();
657
658        if (0 != $subject->getOffsetX()) {
659            // c:x
660            $objWriter->startElement('c:x');
661            $objWriter->writeAttribute('val', $subject->getOffsetX());
662            $objWriter->endElement();
663        }
664
665        if (0 != $subject->getOffsetY()) {
666            // c:y
667            $objWriter->startElement('c:y');
668            $objWriter->writeAttribute('val', $subject->getOffsetY());
669            $objWriter->endElement();
670        }
671
672        if (0 != $subject->getWidth()) {
673            // c:w
674            $objWriter->startElement('c:w');
675            $objWriter->writeAttribute('val', $subject->getWidth());
676            $objWriter->endElement();
677        }
678
679        if (0 != $subject->getHeight()) {
680            // c:h
681            $objWriter->startElement('c:h');
682            $objWriter->writeAttribute('val', $subject->getHeight());
683            $objWriter->endElement();
684        }
685
686        $objWriter->endElement();
687        $objWriter->endElement();
688    }
689
690    /**
691     * Write Type Area.
692     *
693     * @param XMLWriter $objWriter XML Writer
694     * @param Chart\Type\Area $subject
695     *
696     * @throws \Exception
697     */
698    protected function writeTypeArea(XMLWriter $objWriter, Area $subject, bool $includeSheet = false): void
699    {
700        // c:lineChart
701        $objWriter->startElement('c:areaChart');
702
703        // c:grouping
704        $objWriter->startElement('c:grouping');
705        $objWriter->writeAttribute('val', 'standard');
706        $objWriter->endElement();
707
708        // Write series
709        $seriesIndex = 0;
710        foreach ($subject->getSeries() as $series) {
711            // c:ser
712            $objWriter->startElement('c:ser');
713
714            // c:ser > c:idx
715            $objWriter->startElement('c:idx');
716            $objWriter->writeAttribute('val', $seriesIndex);
717            $objWriter->endElement();
718
719            // c:ser > c:order
720            $objWriter->startElement('c:order');
721            $objWriter->writeAttribute('val', $seriesIndex);
722            $objWriter->endElement();
723
724            // c:ser > c:tx
725            $objWriter->startElement('c:tx');
726            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
727            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
728            $objWriter->endElement();
729
730            // c:ser > c:dLbls
731            // @link : https://msdn.microsoft.com/en-us/library/documentformat.openxml.drawing.charts.areachartseries.aspx
732            $objWriter->startElement('c:dLbls');
733
734            // c:ser > c:dLbls > c:showVal
735            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
736
737            // c:ser > c:dLbls > c:showCatName
738            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
739
740            // c:ser > c:dLbls > c:showSerName
741            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
742
743            // c:ser > c:dLbls > c:showPercent
744            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
745
746            // c:ser > ##c:dLbls
747            $objWriter->endElement();
748
749            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
750                // c:spPr
751                $objWriter->startElement('c:spPr');
752                // Write fill
753                $this->writeFill($objWriter, $series->getFill());
754                // ## c:spPr
755                $objWriter->endElement();
756            }
757
758            // Write X axis data
759            $axisXData = array_keys($series->getValues());
760
761            // c:cat
762            $objWriter->startElement('c:cat');
763            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
764            $objWriter->endElement();
765
766            // Write Y axis data
767            $axisYData = array_values($series->getValues());
768
769            // c:val
770            $objWriter->startElement('c:val');
771            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
772            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
773            $objWriter->endElement();
774
775            $objWriter->endElement();
776
777            ++$seriesIndex;
778        }
779
780        // c:axId
781        $objWriter->startElement('c:axId');
782        $objWriter->writeAttribute('val', '52743552');
783        $objWriter->endElement();
784
785        // c:axId
786        $objWriter->startElement('c:axId');
787        $objWriter->writeAttribute('val', '52749440');
788        $objWriter->endElement();
789
790        $objWriter->endElement();
791    }
792
793    /**
794     * Write Type Bar.
795     *
796     * @param XMLWriter $objWriter XML Writer
797     * @param Chart\Type\Bar $subject
798     *
799     * @throws \Exception
800     */
801    protected function writeTypeBar(XMLWriter $objWriter, Bar $subject, bool $includeSheet = false): void
802    {
803        // c:barChart
804        $objWriter->startElement('c:barChart');
805
806        // c:barDir
807        $objWriter->startElement('c:barDir');
808        $objWriter->writeAttribute('val', $subject->getBarDirection());
809        $objWriter->endElement();
810
811        // c:grouping
812        $objWriter->startElement('c:grouping');
813        $objWriter->writeAttribute('val', $subject->getBarGrouping());
814        $objWriter->endElement();
815
816        // Write series
817        $seriesIndex = 0;
818        foreach ($subject->getSeries() as $series) {
819            // c:ser
820            $objWriter->startElement('c:ser');
821
822            // c:idx
823            $objWriter->startElement('c:idx');
824            $objWriter->writeAttribute('val', $seriesIndex);
825            $objWriter->endElement();
826
827            // c:order
828            $objWriter->startElement('c:order');
829            $objWriter->writeAttribute('val', $seriesIndex);
830            $objWriter->endElement();
831
832            // c:tx
833            $objWriter->startElement('c:tx');
834            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
835            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
836            $objWriter->endElement();
837
838            // Fills for points?
839            $dataPointFills = $series->getDataPointFills();
840            foreach ($dataPointFills as $key => $value) {
841                // c:dPt
842                $objWriter->startElement('c:dPt');
843
844                // c:idx
845                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
846
847                if (Fill::FILL_NONE != $value->getFillType()) {
848                    // c:spPr
849                    $objWriter->startElement('c:spPr');
850                    // Write fill
851                    $this->writeFill($objWriter, $value);
852                    // ## c:spPr
853                    $objWriter->endElement();
854                }
855
856                // ## c:dPt
857                $objWriter->endElement();
858            }
859
860            // c:dLbls
861            $objWriter->startElement('c:dLbls');
862
863            if ($series->hasDlblNumFormat()) {
864                //c:numFmt
865                $objWriter->startElement('c:numFmt');
866                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
867                $objWriter->writeAttribute('sourceLinked', '0');
868                $objWriter->endElement();
869            }
870
871            // c:txPr
872            $objWriter->startElement('c:txPr');
873
874            // a:bodyPr
875            $objWriter->writeElement('a:bodyPr');
876
877            // a:lstStyle
878            $objWriter->writeElement('a:lstStyle');
879
880            // a:p
881            $objWriter->startElement('a:p');
882
883            // a:pPr
884            $objWriter->startElement('a:pPr');
885
886            // a:defRPr
887            $objWriter->startElement('a:defRPr');
888
889            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
890            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
891            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
892            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
893            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
894            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
895            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
896
897            // a:solidFill
898            $objWriter->startElement('a:solidFill');
899            $this->writeColor($objWriter, $series->getFont()->getColor());
900            // >a:solidFill
901            $objWriter->endElement();
902            // a:latin
903            $objWriter->startElement('a:latin');
904            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
905            // >a:latin
906            $objWriter->endElement();
907
908            // >a:defRPr
909            $objWriter->endElement();
910            // >a:pPr
911            $objWriter->endElement();
912
913            // a:endParaRPr
914            $objWriter->startElement('a:endParaRPr');
915            $objWriter->writeAttribute('lang', 'en-US');
916            $objWriter->writeAttribute('dirty', '0');
917            $objWriter->endElement();
918
919            // >a:p
920            $objWriter->endElement();
921            // >a:lstStyle
922            $objWriter->endElement();
923
924            // c:dLblPos
925            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
926
927            // c:showVal
928            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
929
930            // c:showCatName
931            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
932
933            // c:showSerName
934            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
935
936            // c:showPercent
937            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
938
939            // c:separator
940            $objWriter->writeElement('c:separator', $series->hasShowSeparator() ? $series->getSeparator() : '');
941
942            // c:showLeaderLines
943            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
944
945            $objWriter->endElement();
946
947            // c:spPr
948            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
949                // c:spPr
950                $objWriter->startElement('c:spPr');
951                // Write fill
952                $this->writeFill($objWriter, $series->getFill());
953                // ## c:spPr
954                $objWriter->endElement();
955            }
956
957            // Write X axis data
958            $axisXData = array_keys($series->getValues());
959
960            // c:cat
961            $objWriter->startElement('c:cat');
962            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
963            $objWriter->endElement();
964
965            // Write Y axis data
966            $axisYData = array_values($series->getValues());
967
968            // c:val
969            $objWriter->startElement('c:val');
970            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
971            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
972            $objWriter->endElement();
973
974            $objWriter->endElement();
975
976            ++$seriesIndex;
977        }
978
979        // c:gapWidth
980        $objWriter->startElement('c:gapWidth');
981        $objWriter->writeAttribute('val', $subject->getGapWidthPercent());
982        $objWriter->endElement();
983
984        // c:overlap
985        $barGrouping = $subject->getBarGrouping();
986        $objWriter->startElement('c:overlap');
987        if (Bar::GROUPING_CLUSTERED === $barGrouping) {
988            $objWriter->writeAttribute('val', '0');
989        } elseif (Bar::GROUPING_STACKED === $barGrouping || Bar::GROUPING_PERCENTSTACKED === $barGrouping) {
990            $objWriter->writeAttribute('val', '100');
991        }
992        $objWriter->endElement();
993
994        // c:axId
995        $objWriter->startElement('c:axId');
996        $objWriter->writeAttribute('val', '52743552');
997        $objWriter->endElement();
998
999        // c:axId
1000        $objWriter->startElement('c:axId');
1001        $objWriter->writeAttribute('val', '52749440');
1002        $objWriter->endElement();
1003
1004        // c:extLst
1005        $objWriter->startElement('c:extLst');
1006        $objWriter->endElement();
1007
1008        $objWriter->endElement();
1009    }
1010
1011    /**
1012     * Write Type Bar3D.
1013     *
1014     * @param XMLWriter $objWriter XML Writer
1015     * @param Chart\Type\Bar3D $subject
1016     *
1017     * @throws \Exception
1018     */
1019    protected function writeTypeBar3D(XMLWriter $objWriter, Bar3D $subject, bool $includeSheet = false): void
1020    {
1021        // c:bar3DChart
1022        $objWriter->startElement('c:bar3DChart');
1023
1024        // c:barDir
1025        $objWriter->startElement('c:barDir');
1026        $objWriter->writeAttribute('val', $subject->getBarDirection());
1027        $objWriter->endElement();
1028
1029        // c:grouping
1030        $objWriter->startElement('c:grouping');
1031        $objWriter->writeAttribute('val', $subject->getBarGrouping());
1032        $objWriter->endElement();
1033
1034        // Write series
1035        $seriesIndex = 0;
1036        foreach ($subject->getSeries() as $series) {
1037            // c:ser
1038            $objWriter->startElement('c:ser');
1039
1040            // c:idx
1041            $objWriter->startElement('c:idx');
1042            $objWriter->writeAttribute('val', $seriesIndex);
1043            $objWriter->endElement();
1044
1045            // c:order
1046            $objWriter->startElement('c:order');
1047            $objWriter->writeAttribute('val', $seriesIndex);
1048            $objWriter->endElement();
1049
1050            // c:tx
1051            $objWriter->startElement('c:tx');
1052            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1053            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1054            $objWriter->endElement();
1055
1056            // Fills for points?
1057            $dataPointFills = $series->getDataPointFills();
1058            foreach ($dataPointFills as $key => $value) {
1059                // c:dPt
1060                $objWriter->startElement('c:dPt');
1061
1062                // c:idx
1063                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1064
1065                if (Fill::FILL_NONE != $value->getFillType()) {
1066                    // c:spPr
1067                    $objWriter->startElement('c:spPr');
1068                    // Write fill
1069                    $this->writeFill($objWriter, $value);
1070                    // ## c:spPr
1071                    $objWriter->endElement();
1072                }
1073
1074                // ## c:dPt
1075                $objWriter->endElement();
1076            }
1077
1078            // c:dLbls
1079            $objWriter->startElement('c:dLbls');
1080
1081            // c:txPr
1082            $objWriter->startElement('c:txPr');
1083
1084            // a:bodyPr
1085            $objWriter->writeElement('a:bodyPr', null);
1086
1087            // a:lstStyle
1088            $objWriter->writeElement('a:lstStyle', null);
1089
1090            // a:p
1091            $objWriter->startElement('a:p');
1092
1093            // a:pPr
1094            $objWriter->startElement('a:pPr');
1095
1096            // a:defRPr
1097            $objWriter->startElement('a:defRPr');
1098
1099            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1100            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1101            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1102            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1103            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1104            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1105            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1106
1107            // Font - a:solidFill
1108            $objWriter->startElement('a:solidFill');
1109
1110            $this->writeColor($objWriter, $series->getFont()->getColor());
1111
1112            $objWriter->endElement();
1113
1114            // Font - a:latin
1115            $objWriter->startElement('a:latin');
1116            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1117            $objWriter->endElement();
1118
1119            $objWriter->endElement();
1120
1121            $objWriter->endElement();
1122
1123            // a:endParaRPr
1124            $objWriter->startElement('a:endParaRPr');
1125            $objWriter->writeAttribute('lang', 'en-US');
1126            $objWriter->writeAttribute('dirty', '0');
1127            $objWriter->endElement();
1128
1129            $objWriter->endElement();
1130
1131            $objWriter->endElement();
1132
1133            // c:showVal
1134            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1135
1136            // c:showCatName
1137            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1138
1139            // c:showSerName
1140            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1141
1142            // c:showPercent
1143            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1144
1145            // c:showLeaderLines
1146            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1147
1148            $objWriter->endElement();
1149
1150            // c:spPr
1151            if (Fill::FILL_NONE != $series->getFill()->getFillType()) {
1152                // c:spPr
1153                $objWriter->startElement('c:spPr');
1154                // Write fill
1155                $this->writeFill($objWriter, $series->getFill());
1156                // ## c:spPr
1157                $objWriter->endElement();
1158            }
1159
1160            // Write X axis data
1161            $axisXData = array_keys($series->getValues());
1162
1163            // c:cat
1164            $objWriter->startElement('c:cat');
1165            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1166            $objWriter->endElement();
1167
1168            // Write Y axis data
1169            $axisYData = array_values($series->getValues());
1170
1171            // c:val
1172            $objWriter->startElement('c:val');
1173            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1174            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1175            $objWriter->endElement();
1176
1177            $objWriter->endElement();
1178
1179            ++$seriesIndex;
1180        }
1181
1182        // c:gapWidth
1183        $objWriter->startElement('c:gapWidth');
1184        $objWriter->writeAttribute('val', $subject->getGapWidthPercent());
1185        $objWriter->endElement();
1186
1187        // c:axId
1188        $objWriter->startElement('c:axId');
1189        $objWriter->writeAttribute('val', '52743552');
1190        $objWriter->endElement();
1191
1192        // c:axId
1193        $objWriter->startElement('c:axId');
1194        $objWriter->writeAttribute('val', '52749440');
1195        $objWriter->endElement();
1196
1197        // c:axId
1198        $objWriter->startElement('c:axId');
1199        $objWriter->writeAttribute('val', '0');
1200        $objWriter->endElement();
1201
1202        $objWriter->endElement();
1203    }
1204
1205    /**
1206     * Write Type Pie.
1207     *
1208     * @param XMLWriter $objWriter XML Writer
1209     *
1210     * @throws \Exception
1211     */
1212    protected function writeTypeDoughnut(XMLWriter $objWriter, Doughnut $subject, bool $includeSheet = false): void
1213    {
1214        // c:pieChart
1215        $objWriter->startElement('c:doughnutChart');
1216
1217        // c:varyColors
1218        $objWriter->startElement('c:varyColors');
1219        $objWriter->writeAttribute('val', '1');
1220        $objWriter->endElement();
1221
1222        // Write series
1223        $seriesIndex = 0;
1224        foreach ($subject->getSeries() as $series) {
1225            // c:ser
1226            $objWriter->startElement('c:ser');
1227
1228            // c:idx
1229            $objWriter->startElement('c:idx');
1230            $objWriter->writeAttribute('val', $seriesIndex);
1231            $objWriter->endElement();
1232
1233            // c:order
1234            $objWriter->startElement('c:order');
1235            $objWriter->writeAttribute('val', $seriesIndex);
1236            $objWriter->endElement();
1237
1238            // c:tx
1239            $objWriter->startElement('c:tx');
1240            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1241            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1242            $objWriter->endElement();
1243
1244            // Fills for points?
1245            $dataPointFills = $series->getDataPointFills();
1246            foreach ($dataPointFills as $key => $value) {
1247                // c:dPt
1248                $objWriter->startElement('c:dPt');
1249                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1250                // c:dPt/c:spPr
1251                $objWriter->startElement('c:spPr');
1252                $this->writeFill($objWriter, $value);
1253                // c:dPt/##c:spPr
1254                $objWriter->endElement();
1255                // ##c:dPt
1256                $objWriter->endElement();
1257            }
1258
1259            // Write X axis data
1260            $axisXData = array_keys($series->getValues());
1261
1262            // c:cat
1263            $objWriter->startElement('c:cat');
1264            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1265            $objWriter->endElement();
1266
1267            // Write Y axis data
1268            $axisYData = array_values($series->getValues());
1269
1270            // c:val
1271            $objWriter->startElement('c:val');
1272            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1273            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1274            $objWriter->endElement();
1275
1276            $objWriter->endElement();
1277
1278            ++$seriesIndex;
1279        }
1280
1281        if (isset($series) && is_object($series) && $series instanceof Chart\Series) {
1282            // c:dLbls
1283            $objWriter->startElement('c:dLbls');
1284
1285            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1286            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1287            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1288            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1289            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1290            $this->writeElementWithValAttribute($objWriter, 'c:showBubbleSize', '0');
1291            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1292
1293            if ($series->hasDlblNumFormat()) {
1294                //c:numFmt
1295                $objWriter->startElement('c:numFmt');
1296                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
1297                $objWriter->writeAttribute('sourceLinked', '0');
1298                $objWriter->endElement();
1299            }
1300
1301            // c:dLbls\c:txPr
1302            $objWriter->startElement('c:txPr');
1303            $objWriter->writeElement('a:bodyPr', null);
1304            $objWriter->writeElement('a:lstStyle', null);
1305
1306            // c:dLbls\c:txPr\a:p
1307            $objWriter->startElement('a:p');
1308
1309            // c:dLbls\c:txPr\a:p\a:pPr
1310            $objWriter->startElement('a:pPr');
1311
1312            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr
1313            $objWriter->startElement('a:defRPr');
1314            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1315            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1316            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1317            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1318            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1319            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1320            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1321
1322            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:solidFill
1323            $objWriter->startElement('a:solidFill');
1324            $this->writeColor($objWriter, $series->getFont()->getColor());
1325            $objWriter->endElement();
1326
1327            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\a:latin
1328            $objWriter->startElement('a:latin');
1329            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1330            $objWriter->endElement();
1331
1332            // c:dLbls\c:txPr\a:p\a:pPr\a:defRPr\
1333            $objWriter->endElement();
1334            // c:dLbls\c:txPr\a:p\a:pPr\
1335            $objWriter->endElement();
1336
1337            // c:dLbls\c:txPr\a:p\a:endParaRPr
1338            $objWriter->startElement('a:endParaRPr');
1339            $objWriter->writeAttribute('lang', 'en-US');
1340            $objWriter->writeAttribute('dirty', '0');
1341            $objWriter->endElement();
1342
1343            // c:dLbls\c:txPr\a:p\
1344            $objWriter->endElement();
1345            // c:dLbls\c:txPr\
1346            $objWriter->endElement();
1347
1348            $separator = $series->getSeparator();
1349            if (!empty($separator) && PHP_EOL != $separator) {
1350                // c:dLbls\c:separator
1351                $objWriter->writeElement('c:separator', $separator);
1352            }
1353
1354            // c:dLbls\
1355            $objWriter->endElement();
1356        }
1357
1358        $this->writeElementWithValAttribute($objWriter, 'c:firstSliceAng', '0');
1359        $this->writeElementWithValAttribute($objWriter, 'c:holeSize', (string) $subject->getHoleSize());
1360
1361        $objWriter->endElement();
1362    }
1363
1364    /**
1365     * Write Type Pie.
1366     *
1367     * @param XMLWriter $objWriter XML Writer
1368     *
1369     * @throws \Exception
1370     */
1371    protected function writeTypePie(XMLWriter $objWriter, Pie $subject, bool $includeSheet = false): void
1372    {
1373        // c:pieChart
1374        $objWriter->startElement('c:pieChart');
1375
1376        // c:varyColors
1377        $objWriter->startElement('c:varyColors');
1378        $objWriter->writeAttribute('val', '1');
1379        $objWriter->endElement();
1380
1381        // Write series
1382        $seriesIndex = 0;
1383        foreach ($subject->getSeries() as $series) {
1384            // c:ser
1385            $objWriter->startElement('c:ser');
1386
1387            // c:idx
1388            $objWriter->startElement('c:idx');
1389            $objWriter->writeAttribute('val', $seriesIndex);
1390            $objWriter->endElement();
1391
1392            // c:order
1393            $objWriter->startElement('c:order');
1394            $objWriter->writeAttribute('val', $seriesIndex);
1395            $objWriter->endElement();
1396
1397            // c:tx
1398            $objWriter->startElement('c:tx');
1399            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1400            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1401            $objWriter->endElement();
1402
1403            // Fills for points?
1404            $dataPointFills = $series->getDataPointFills();
1405            foreach ($dataPointFills as $key => $value) {
1406                // c:dPt
1407                $objWriter->startElement('c:dPt');
1408                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1409                // c:dPt/c:spPr
1410                $objWriter->startElement('c:spPr');
1411                $this->writeFill($objWriter, $value);
1412                // c:dPt/##c:spPr
1413                $objWriter->endElement();
1414                // ##c:dPt
1415                $objWriter->endElement();
1416            }
1417
1418            // c:dLbls
1419            $objWriter->startElement('c:dLbls');
1420
1421            if ($series->hasDlblNumFormat()) {
1422                //c:numFmt
1423                $objWriter->startElement('c:numFmt');
1424                $objWriter->writeAttribute('formatCode', $series->getDlblNumFormat());
1425                $objWriter->writeAttribute('sourceLinked', '0');
1426                $objWriter->endElement();
1427            }
1428
1429            // c:txPr
1430            $objWriter->startElement('c:txPr');
1431
1432            // a:bodyPr
1433            $objWriter->writeElement('a:bodyPr', null);
1434
1435            // a:lstStyle
1436            $objWriter->writeElement('a:lstStyle', null);
1437
1438            // a:p
1439            $objWriter->startElement('a:p');
1440
1441            // a:pPr
1442            $objWriter->startElement('a:pPr');
1443
1444            // a:defRPr
1445            $objWriter->startElement('a:defRPr');
1446
1447            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1448            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1449            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1450            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1451            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1452            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1453            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1454
1455            // Font - a:solidFill
1456            $objWriter->startElement('a:solidFill');
1457
1458            $this->writeColor($objWriter, $series->getFont()->getColor());
1459
1460            $objWriter->endElement();
1461
1462            // Font - a:latin
1463            $objWriter->startElement('a:latin');
1464            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1465            $objWriter->endElement();
1466
1467            $objWriter->endElement();
1468
1469            $objWriter->endElement();
1470
1471            // a:endParaRPr
1472            $objWriter->startElement('a:endParaRPr');
1473            $objWriter->writeAttribute('lang', 'en-US');
1474            $objWriter->writeAttribute('dirty', '0');
1475            $objWriter->endElement();
1476
1477            $objWriter->endElement();
1478
1479            $objWriter->endElement();
1480
1481            // c:dLblPos
1482            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
1483
1484            // c:showLegendKey
1485            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1486
1487            // c:showVal
1488            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1489
1490            // c:showCatName
1491            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1492
1493            // c:showSerName
1494            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1495
1496            // c:showPercent
1497            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1498
1499            // c:showLeaderLines
1500            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1501
1502            $objWriter->endElement();
1503
1504            // Write X axis data
1505            $axisXData = array_keys($series->getValues());
1506
1507            // c:cat
1508            $objWriter->startElement('c:cat');
1509            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1510            $objWriter->endElement();
1511
1512            // Write Y axis data
1513            $axisYData = array_values($series->getValues());
1514
1515            // c:val
1516            $objWriter->startElement('c:val');
1517            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1518            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1519            $objWriter->endElement();
1520
1521            $objWriter->endElement();
1522
1523            ++$seriesIndex;
1524        }
1525
1526        $objWriter->endElement();
1527    }
1528
1529    /**
1530     * Write Type Pie3D.
1531     *
1532     * @param XMLWriter $objWriter XML Writer
1533     *
1534     * @throws \Exception
1535     */
1536    protected function writeTypePie3D(XMLWriter $objWriter, Pie3D $subject, bool $includeSheet = false): void
1537    {
1538        // c:pie3DChart
1539        $objWriter->startElement('c:pie3DChart');
1540
1541        // c:varyColors
1542        $objWriter->startElement('c:varyColors');
1543        $objWriter->writeAttribute('val', '1');
1544        $objWriter->endElement();
1545
1546        // Write series
1547        $seriesIndex = 0;
1548        foreach ($subject->getSeries() as $series) {
1549            // c:ser
1550            $objWriter->startElement('c:ser');
1551
1552            // c:idx
1553            $objWriter->startElement('c:idx');
1554            $objWriter->writeAttribute('val', $seriesIndex);
1555            $objWriter->endElement();
1556
1557            // c:order
1558            $objWriter->startElement('c:order');
1559            $objWriter->writeAttribute('val', $seriesIndex);
1560            $objWriter->endElement();
1561
1562            // c:tx
1563            $objWriter->startElement('c:tx');
1564            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1565            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1566            $objWriter->endElement();
1567
1568            // c:explosion
1569            $objWriter->startElement('c:explosion');
1570            $objWriter->writeAttribute('val', $subject->getExplosion());
1571            $objWriter->endElement();
1572
1573            // Fills for points?
1574            $dataPointFills = $series->getDataPointFills();
1575            foreach ($dataPointFills as $key => $value) {
1576                // c:dPt
1577                $objWriter->startElement('c:dPt');
1578                $this->writeElementWithValAttribute($objWriter, 'c:idx', $key);
1579                // c:dPt/c:spPr
1580                $objWriter->startElement('c:spPr');
1581                $this->writeFill($objWriter, $value);
1582                // c:dPt/##c:spPr
1583                $objWriter->endElement();
1584                // ##c:dPt
1585                $objWriter->endElement();
1586            }
1587
1588            // c:dLbls
1589            $objWriter->startElement('c:dLbls');
1590
1591            // c:txPr
1592            $objWriter->startElement('c:txPr');
1593
1594            // a:bodyPr
1595            $objWriter->writeElement('a:bodyPr', null);
1596
1597            // a:lstStyle
1598            $objWriter->writeElement('a:lstStyle', null);
1599
1600            // a:p
1601            $objWriter->startElement('a:p');
1602
1603            // a:pPr
1604            $objWriter->startElement('a:pPr');
1605
1606            // a:defRPr
1607            $objWriter->startElement('a:defRPr');
1608
1609            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1610            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1611            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1612            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1613            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1614            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1615            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1616
1617            // Font - a:solidFill
1618            $objWriter->startElement('a:solidFill');
1619
1620            $this->writeColor($objWriter, $series->getFont()->getColor());
1621
1622            $objWriter->endElement();
1623
1624            // Font - a:latin
1625            $objWriter->startElement('a:latin');
1626            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1627            $objWriter->endElement();
1628
1629            $objWriter->endElement();
1630
1631            $objWriter->endElement();
1632
1633            // a:endParaRPr
1634            $objWriter->startElement('a:endParaRPr');
1635            $objWriter->writeAttribute('lang', 'en-US');
1636            $objWriter->writeAttribute('dirty', '0');
1637            $objWriter->endElement();
1638
1639            $objWriter->endElement();
1640
1641            $objWriter->endElement();
1642
1643            // c:dLblPos
1644            $this->writeElementWithValAttribute($objWriter, 'c:dLblPos', $series->getLabelPosition());
1645
1646            // c:showVal
1647            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1648
1649            // c:showCatName
1650            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1651
1652            // c:showSerName
1653            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1654
1655            // c:showPercent
1656            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1657
1658            // c:showLeaderLines
1659            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1660
1661            $objWriter->endElement();
1662
1663            // Write X axis data
1664            $axisXData = array_keys($series->getValues());
1665
1666            // c:cat
1667            $objWriter->startElement('c:cat');
1668            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1669            $objWriter->endElement();
1670
1671            // Write Y axis data
1672            $axisYData = array_values($series->getValues());
1673
1674            // c:val
1675            $objWriter->startElement('c:val');
1676            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1677            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1678            $objWriter->endElement();
1679
1680            $objWriter->endElement();
1681
1682            ++$seriesIndex;
1683        }
1684
1685        $objWriter->endElement();
1686    }
1687
1688    /**
1689     * Write Type Line.
1690     *
1691     * @param XMLWriter $objWriter XML Writer
1692     *
1693     * @throws \Exception
1694     */
1695    protected function writeTypeLine(XMLWriter $objWriter, Line $subject, bool $includeSheet = false): void
1696    {
1697        // c:lineChart
1698        $objWriter->startElement('c:lineChart');
1699
1700        // c:grouping
1701        $objWriter->startElement('c:grouping');
1702        $objWriter->writeAttribute('val', 'standard');
1703        $objWriter->endElement();
1704
1705        // Write series
1706        $seriesIndex = 0;
1707        foreach ($subject->getSeries() as $series) {
1708            // c:ser
1709            $objWriter->startElement('c:ser');
1710
1711            // c:idx
1712            $objWriter->startElement('c:idx');
1713            $objWriter->writeAttribute('val', $seriesIndex);
1714            $objWriter->endElement();
1715
1716            // c:order
1717            $objWriter->startElement('c:order');
1718            $objWriter->writeAttribute('val', $seriesIndex);
1719            $objWriter->endElement();
1720
1721            // c:tx
1722            $objWriter->startElement('c:tx');
1723            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1724            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1725            $objWriter->endElement();
1726
1727            // c:spPr
1728            $objWriter->startElement('c:spPr');
1729            // Write fill
1730            $this->writeFill($objWriter, $series->getFill());
1731            // Write outline
1732            $this->writeOutline($objWriter, $series->getOutline());
1733            // ## c:spPr
1734            $objWriter->endElement();
1735
1736            // Marker
1737            $this->writeSeriesMarker($objWriter, $series->getMarker());
1738
1739            // c:dLbls
1740            $objWriter->startElement('c:dLbls');
1741
1742            // c:txPr
1743            $objWriter->startElement('c:txPr');
1744
1745            // a:bodyPr
1746            $objWriter->writeElement('a:bodyPr', null);
1747
1748            // a:lstStyle
1749            $objWriter->writeElement('a:lstStyle', null);
1750
1751            // a:p
1752            $objWriter->startElement('a:p');
1753
1754            // a:pPr
1755            $objWriter->startElement('a:pPr');
1756
1757            // a:defRPr
1758            $objWriter->startElement('a:defRPr');
1759
1760            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1761            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1762            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1763            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1764            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1765            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1766            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1767
1768            // Font - a:solidFill
1769            $objWriter->startElement('a:solidFill');
1770
1771            $this->writeColor($objWriter, $series->getFont()->getColor());
1772
1773            $objWriter->endElement();
1774
1775            // Font - a:latin
1776            $objWriter->startElement('a:latin');
1777            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1778            $objWriter->endElement();
1779
1780            $objWriter->endElement();
1781
1782            $objWriter->endElement();
1783
1784            // a:endParaRPr
1785            $objWriter->startElement('a:endParaRPr');
1786            $objWriter->writeAttribute('lang', 'en-US');
1787            $objWriter->writeAttribute('dirty', '0');
1788            $objWriter->endElement();
1789
1790            $objWriter->endElement();
1791
1792            $objWriter->endElement();
1793
1794            // c:showVal
1795            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1796
1797            // c:showCatName
1798            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1799
1800            // c:showSerName
1801            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1802
1803            // c:showPercent
1804            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1805
1806            // c:showLeaderLines
1807            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1808
1809            // > c:dLbls
1810            $objWriter->endElement();
1811
1812            // Write X axis data
1813            $axisXData = array_keys($series->getValues());
1814
1815            // c:cat
1816            $objWriter->startElement('c:cat');
1817            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
1818            $objWriter->endElement();
1819
1820            // Write Y axis data
1821            $axisYData = array_values($series->getValues());
1822
1823            // c:val
1824            $objWriter->startElement('c:val');
1825            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
1826            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
1827            $objWriter->endElement();
1828
1829            $objWriter->endElement();
1830
1831            ++$seriesIndex;
1832        }
1833
1834        // c:marker
1835        $objWriter->startElement('c:marker');
1836        $objWriter->writeAttribute('val', '1');
1837        $objWriter->endElement();
1838
1839        // c:smooth
1840        $objWriter->startElement('c:smooth');
1841        $objWriter->writeAttribute('val', '0');
1842        $objWriter->endElement();
1843
1844        // c:axId
1845        $objWriter->startElement('c:axId');
1846        $objWriter->writeAttribute('val', '52743552');
1847        $objWriter->endElement();
1848
1849        // c:axId
1850        $objWriter->startElement('c:axId');
1851        $objWriter->writeAttribute('val', '52749440');
1852        $objWriter->endElement();
1853
1854        $objWriter->endElement();
1855    }
1856
1857    /**
1858     * Write Type Scatter.
1859     *
1860     * @param XMLWriter $objWriter XML Writer
1861     * @param Chart\Type\Scatter $subject
1862     *
1863     * @throws \Exception
1864     */
1865    protected function writeTypeScatter(XMLWriter $objWriter, Scatter $subject, bool $includeSheet = false): void
1866    {
1867        // c:scatterChart
1868        $objWriter->startElement('c:scatterChart');
1869
1870        // c:scatterStyle
1871        $objWriter->startElement('c:scatterStyle');
1872        $objWriter->writeAttribute('val', 'lineMarker');
1873        $objWriter->endElement();
1874
1875        // c:varyColors
1876        $objWriter->startElement('c:varyColors');
1877        $objWriter->writeAttribute('val', '0');
1878        $objWriter->endElement();
1879
1880        // Write series
1881        $seriesIndex = 0;
1882        foreach ($subject->getSeries() as $series) {
1883            // c:ser
1884            $objWriter->startElement('c:ser');
1885
1886            // c:idx
1887            $objWriter->startElement('c:idx');
1888            $objWriter->writeAttribute('val', $seriesIndex);
1889            $objWriter->endElement();
1890
1891            // c:order
1892            $objWriter->startElement('c:order');
1893            $objWriter->writeAttribute('val', $seriesIndex);
1894            $objWriter->endElement();
1895
1896            // c:tx
1897            $objWriter->startElement('c:tx');
1898            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex(1 + $seriesIndex) . '$1' : '');
1899            $this->writeSingleValueOrReference($objWriter, $includeSheet, $series->getTitle(), $coords);
1900            $objWriter->endElement();
1901
1902            // c:spPr
1903            $objWriter->startElement('c:spPr');
1904            // Write fill
1905            $this->writeFill($objWriter, $series->getFill());
1906            // Write outline
1907            $this->writeOutline($objWriter, $series->getOutline());
1908            // ## c:spPr
1909            $objWriter->endElement();
1910
1911            // Marker
1912            $this->writeSeriesMarker($objWriter, $series->getMarker());
1913
1914            // c:dLbls
1915            $objWriter->startElement('c:dLbls');
1916
1917            // c:txPr
1918            $objWriter->startElement('c:txPr');
1919
1920            // a:bodyPr
1921            $objWriter->writeElement('a:bodyPr', null);
1922
1923            // a:lstStyle
1924            $objWriter->writeElement('a:lstStyle', null);
1925
1926            // a:p
1927            $objWriter->startElement('a:p');
1928
1929            // a:pPr
1930            $objWriter->startElement('a:pPr');
1931
1932            // a:defRPr
1933            $objWriter->startElement('a:defRPr');
1934
1935            $objWriter->writeAttribute('b', ($series->getFont()->isBold() ? 'true' : 'false'));
1936            $objWriter->writeAttribute('i', ($series->getFont()->isItalic() ? 'true' : 'false'));
1937            $objWriter->writeAttribute('strike', ($series->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
1938            $objWriter->writeAttribute('sz', ($series->getFont()->getSize() * 100));
1939            $objWriter->writeAttribute('u', $series->getFont()->getUnderline());
1940            $objWriter->writeAttributeIf($series->getFont()->isSuperScript(), 'baseline', '300000');
1941            $objWriter->writeAttributeIf($series->getFont()->isSubScript(), 'baseline', '-250000');
1942
1943            // Font - a:solidFill
1944            $objWriter->startElement('a:solidFill');
1945
1946            $this->writeColor($objWriter, $series->getFont()->getColor());
1947
1948            $objWriter->endElement();
1949
1950            // Font - a:latin
1951            $objWriter->startElement('a:latin');
1952            $objWriter->writeAttribute('typeface', $series->getFont()->getName());
1953            $objWriter->endElement();
1954
1955            $objWriter->endElement();
1956
1957            $objWriter->endElement();
1958
1959            // a:endParaRPr
1960            $objWriter->startElement('a:endParaRPr');
1961            $objWriter->writeAttribute('lang', 'en-US');
1962            $objWriter->writeAttribute('dirty', '0');
1963            $objWriter->endElement();
1964
1965            $objWriter->endElement();
1966
1967            $objWriter->endElement();
1968
1969            // c:showLegendKey
1970            $this->writeElementWithValAttribute($objWriter, 'c:showLegendKey', $series->hasShowLegendKey() ? '1' : '0');
1971
1972            // c:showVal
1973            $this->writeElementWithValAttribute($objWriter, 'c:showVal', $series->hasShowValue() ? '1' : '0');
1974
1975            // c:showCatName
1976            $this->writeElementWithValAttribute($objWriter, 'c:showCatName', $series->hasShowCategoryName() ? '1' : '0');
1977
1978            // c:showSerName
1979            $this->writeElementWithValAttribute($objWriter, 'c:showSerName', $series->hasShowSeriesName() ? '1' : '0');
1980
1981            // c:showPercent
1982            $this->writeElementWithValAttribute($objWriter, 'c:showPercent', $series->hasShowPercentage() ? '1' : '0');
1983
1984            // c:separator
1985            $separator = $series->getSeparator();
1986            if (!empty($separator) && PHP_EOL != $separator) {
1987                // c:dLbls\c:separator
1988                $objWriter->writeElement('c:separator', $separator);
1989            }
1990
1991            // c:showLeaderLines
1992            $this->writeElementWithValAttribute($objWriter, 'c:showLeaderLines', $series->hasShowLeaderLines() ? '1' : '0');
1993
1994            $objWriter->endElement();
1995
1996            // Write X axis data
1997            $axisXData = array_keys($series->getValues());
1998
1999            // c:xVal
2000            $objWriter->startElement('c:xVal');
2001            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisXData, 'Sheet1!$A$2:$A$' . (1 + count($axisXData)));
2002            $objWriter->endElement();
2003
2004            // Write Y axis data
2005            $axisYData = array_values($series->getValues());
2006
2007            // c:yVal
2008            $objWriter->startElement('c:yVal');
2009            $coords = ($includeSheet ? 'Sheet1!$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$2:$' . Coordinate::stringFromColumnIndex($seriesIndex + 1) . '$' . (1 + count($axisYData)) : '');
2010            $this->writeMultipleValuesOrReference($objWriter, $includeSheet, $axisYData, $coords);
2011            $objWriter->endElement();
2012
2013            // c:smooth
2014            $objWriter->startElement('c:smooth');
2015            $objWriter->writeAttribute('val', '0');
2016            $objWriter->endElement();
2017
2018            $objWriter->endElement();
2019
2020            ++$seriesIndex;
2021        }
2022
2023        // c:axId
2024        $objWriter->startElement('c:axId');
2025        $objWriter->writeAttribute('val', '52743552');
2026        $objWriter->endElement();
2027
2028        // c:axId
2029        $objWriter->startElement('c:axId');
2030        $objWriter->writeAttribute('val', '52749440');
2031        $objWriter->endElement();
2032
2033        $objWriter->endElement();
2034    }
2035
2036    /**
2037     * Write chart relationships to XML format.
2038     *
2039     * @return string XML Output
2040     *
2041     * @throws \Exception
2042     */
2043    public function writeChartRelationships(Chart $pChart): string
2044    {
2045        // Create XML writer
2046        $objWriter = new XMLWriter(XMLWriter::STORAGE_MEMORY);
2047
2048        // XML header
2049        $objWriter->startDocument('1.0', 'UTF-8', 'yes');
2050
2051        // Relationships
2052        $objWriter->startElement('Relationships');
2053        $objWriter->writeAttribute('xmlns', 'http://schemas.openxmlformats.org/package/2006/relationships');
2054
2055        // Write spreadsheet relationship?
2056        if ($pChart->hasIncludedSpreadsheet()) {
2057            $this->writeRelationship($objWriter, 1, 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/package', '../embeddings/' . $pChart->getIndexedFilename() . '.xlsx');
2058        }
2059
2060        $objWriter->endElement();
2061
2062        // Return
2063        return $objWriter->getData();
2064    }
2065
2066    protected function writeSeriesMarker(XMLWriter $objWriter, Chart\Marker $marker): void
2067    {
2068        // c:marker
2069        $objWriter->startElement('c:marker');
2070        // c:marker > c:symbol
2071        $objWriter->startElement('c:symbol');
2072        $objWriter->writeAttribute('val', $marker->getSymbol());
2073        $objWriter->endElement();
2074
2075        // Size if different of none
2076        if (Chart\Marker::SYMBOL_NONE != $marker->getSymbol()) {
2077            $markerSize = (int) $marker->getSize();
2078            if ($markerSize < 2) {
2079                $markerSize = 2;
2080            }
2081            if ($markerSize > 72) {
2082                $markerSize = 72;
2083            }
2084
2085            /*
2086             * c:marker > c:size
2087             * Size in points
2088             * @link : https://msdn.microsoft.com/en-us/library/hh658135(v=office.12).aspx
2089             */
2090            $objWriter->startElement('c:size');
2091            $objWriter->writeAttribute('val', $markerSize);
2092            $objWriter->endElement();
2093        }
2094
2095        // // c:marker > c:spPr
2096        $objWriter->startElement('c:spPr');
2097        $this->writeFill($objWriter, $marker->getFill());
2098        $this->writeBorder($objWriter, $marker->getBorder(), '', true);
2099        $objWriter->endElement();
2100
2101        // > c:marker
2102        $objWriter->endElement();
2103    }
2104
2105    /**
2106     * @throws \Exception
2107     */
2108    protected function writeAxis(XMLWriter $objWriter, Chart\Axis $oAxis, string $typeAxis, Chart\Type\AbstractType $typeChart): void
2109    {
2110        if (Chart\Axis::AXIS_X != $typeAxis && Chart\Axis::AXIS_Y != $typeAxis) {
2111            return;
2112        }
2113
2114        if (Chart\Axis::AXIS_X == $typeAxis) {
2115            $mainElement = 'c:catAx';
2116            $axIdVal = '52743552';
2117            $axPosVal = 'b';
2118            $crossAxVal = '52749440';
2119        } else {
2120            $mainElement = 'c:valAx';
2121            $axIdVal = '52749440';
2122            $axPosVal = 'l';
2123            $crossAxVal = '52743552';
2124        }
2125
2126        // $mainElement
2127        $objWriter->startElement($mainElement);
2128
2129        // $mainElement > c:axId
2130        $objWriter->startElement('c:axId');
2131        $objWriter->writeAttribute('val', $axIdVal);
2132        $objWriter->endElement();
2133
2134        // $mainElement > c:scaling
2135        $objWriter->startElement('c:scaling');
2136
2137        // $mainElement > c:scaling > c:orientation
2138        $objWriter->startElement('c:orientation');
2139        $objWriter->writeAttribute('val', 'minMax');
2140        $objWriter->endElement();
2141
2142        if (null != $oAxis->getMaxBounds()) {
2143            $objWriter->startElement('c:max');
2144            $objWriter->writeAttribute('val', $oAxis->getMaxBounds());
2145            $objWriter->endElement();
2146        }
2147
2148        if (null != $oAxis->getMinBounds()) {
2149            $objWriter->startElement('c:min');
2150            $objWriter->writeAttribute('val', $oAxis->getMinBounds());
2151            $objWriter->endElement();
2152        }
2153
2154        // $mainElement > ##c:scaling
2155        $objWriter->endElement();
2156
2157        // $mainElement > c:delete
2158        $objWriter->startElement('c:delete');
2159        $objWriter->writeAttribute('val', $oAxis->isVisible() ? '0' : '1');
2160        $objWriter->endElement();
2161
2162        // $mainElement > c:axPos
2163        $objWriter->startElement('c:axPos');
2164        $objWriter->writeAttribute('val', $axPosVal);
2165        $objWriter->endElement();
2166
2167        $oMajorGridlines = $oAxis->getMajorGridlines();
2168        if ($oMajorGridlines instanceof Gridlines) {
2169            $objWriter->startElement('c:majorGridlines');
2170
2171            $this->writeAxisGridlines($objWriter, $oMajorGridlines);
2172
2173            $objWriter->endElement();
2174        }
2175
2176        $oMinorGridlines = $oAxis->getMinorGridlines();
2177        if ($oMinorGridlines instanceof Gridlines) {
2178            $objWriter->startElement('c:minorGridlines');
2179
2180            $this->writeAxisGridlines($objWriter, $oMinorGridlines);
2181
2182            $objWriter->endElement();
2183        }
2184
2185        if ('' != $oAxis->getTitle()) {
2186            // c:title
2187            $objWriter->startElement('c:title');
2188
2189            // c:tx
2190            $objWriter->startElement('c:tx');
2191
2192            // c:rich
2193            $objWriter->startElement('c:rich');
2194
2195            // a:bodyPr
2196            $objWriter->startElement('a:bodyPr');
2197            $objWriter->writeAttributeIf($oAxis->getTitleRotation() != 0, 'rot', CommonDrawing::degreesToAngle($oAxis->getTitleRotation()));
2198            $objWriter->endElement();
2199
2200            // a:lstStyle
2201            $objWriter->writeElement('a:lstStyle', null);
2202
2203            // a:p
2204            $objWriter->startElement('a:p');
2205
2206            // a:pPr
2207            $objWriter->startElement('a:pPr');
2208
2209            // a:defRPr
2210            $objWriter->startElement('a:defRPr');
2211
2212            $objWriter->writeAttribute('b', ($oAxis->getFont()->isBold() ? 'true' : 'false'));
2213            $objWriter->writeAttribute('i', ($oAxis->getFont()->isItalic() ? 'true' : 'false'));
2214            $objWriter->writeAttribute('strike', ($oAxis->getFont()->isStrikethrough() ? 'sngStrike' : 'noStrike'));
2215            $objWriter->writeAttribute('sz', ($oAxis->getFont()->getSize() * 100));
2216            $objWriter->writeAttribute('u', $oAxis->getFont()->getUnderline());
2217            $objWriter->writeAttributeIf($oAxis->getFont()->isSuperScript(), 'baseline', '300000');
2218            $objWriter->writeAttributeIf($oAxis->getFont()->isSubScript(), 'baseline', '-250000');
2219
2220            // Font - a:solidFill
2221            $objWriter->startElement('a:solidFill');
2222            $this->writeColor($objWriter, $oAxis->getFont()->getColor());
2223            $objWriter->endElement();
2224
2225            // Font - a:latin
2226            $objWriter->startElement('a:latin');
2227            $objWriter->writeAttribute('typeface', $oAxis->getFont()->getName());
2228            $objWriter->endElement();
2229
2230            $objWriter->endElement();
2231
2232            // ## a:pPr
2233            $objWriter->endElement();
2234
2235            // a:r
2236            $objWriter->startElement('a:r');
2237
2238            // a:rPr
2239            $objWriter->startElement('a:rPr');
2240            $objWriter->writeAttribute('lang', 'en-US');
2241            $objWriter->writeAttribute('dirty', '0');
2242            $objWriter->endElement();
2243
2244            // a:t
2245            $objWriter->writeElement('a:t', $oAxis->getTitle());
2246
2247            // ## a:r
2248            $objWriter->endElement();
2249
2250            // a:endParaRPr
2251            $objWriter->startElement('a:endParaRPr');
2252            $objWriter->writeAttribute('lang', 'en-US');
2253            $objWriter->writeAttribute('dirty', '0');
2254            $objWriter->endElement();
2255
2256            // ## a:p
2257            $objWriter->endElement();
2258
2259            // ## c:rich
2260            $objWriter->endElement();
2261
2262            // ## c:tx
2263            $objWriter->endElement();
2264
2265            // ## c:title
2266            $objWriter->endElement();
2267        }
2268
2269        // c:numFmt
2270        $objWriter->startElement('c:numFmt');
2271        $objWriter->writeAttribute('formatCode', $oAxis->getFormatCode());
2272        $objWriter->writeAttribute('sourceLinked', '1');
2273        $objWriter->endElement();
2274
2275        // c:majorTickMark
2276        $objWriter->startElement('c:majorTickMark');
2277        $objWriter->writeAttribute('val', $oAxis->getMajorTickMark());
2278        $objWriter->endElement();
2279
2280        // c:minorTickMark
2281        $objWriter->startElement('c:minorTickMark');
2282        $objWriter->writeAttribute('val', $oAxis->getMinorTickMark());
2283        $objWriter->endElement();
2284
2285        // c:tickLblPos
2286        $objWriter->startElement('c:tickLblPos');
2287        $objWriter->writeAttribute('val', $oAxis->getTickLabelPosition());
2288        $objWriter->endElement();
2289
2290        // c:spPr
2291        $objWriter->startElement('c:spPr');
2292        // Outline
2293        $this->writeOutline($objWriter, $oAxis->getOutline());
2294        // ##c:spPr
2295        $objWriter->endElement();
2296
2297        // c:crossAx
2298        $objWriter->startElement('c:crossAx');
2299        $objWriter->writeAttribute('val', $crossAxVal);
2300        $objWriter->endElement();
2301
2302        // c:crosses
2303        $objWriter->startElement('c:crosses');
2304        $objWriter->writeAttribute('val', 'autoZero');
2305        $objWriter->endElement();
2306
2307        if (Chart\Axis::AXIS_X == $typeAxis) {
2308            // c:lblAlgn
2309            $objWriter->startElement('c:lblAlgn');
2310            $objWriter->writeAttribute('val', 'ctr');
2311            $objWriter->endElement();
2312
2313            // c:lblOffset
2314            $objWriter->startElement('c:lblOffset');
2315            $objWriter->writeAttribute('val', '100');
2316            $objWriter->endElement();
2317        }
2318
2319        if (Chart\Axis::AXIS_Y == $typeAxis) {
2320            // c:crossBetween
2321            $objWriter->startElement('c:crossBetween');
2322            // midCat : Position Axis On Tick Marks
2323            // between : Between Tick Marks
2324            if ($typeChart instanceof Area) {
2325                $objWriter->writeAttribute('val', 'midCat');
2326            } else {
2327                $objWriter->writeAttribute('val', 'between');
2328            }
2329            $objWriter->endElement();
2330
2331            // c:majorUnit
2332            if (null != $oAxis->getMajorUnit()) {
2333                $objWriter->startElement('c:majorUnit');
2334                $objWriter->writeAttribute('val', $oAxis->getMajorUnit());
2335                $objWriter->endElement();
2336            }
2337
2338            // c:minorUnit
2339            if (null != $oAxis->getMinorUnit()) {
2340                $objWriter->startElement('c:minorUnit');
2341                $objWriter->writeAttribute('val', $oAxis->getMinorUnit());
2342                $objWriter->endElement();
2343            }
2344        }
2345
2346        $objWriter->endElement();
2347    }
2348
2349    /**
2350     * @throws \Exception
2351     */
2352    protected function writeAxisGridlines(XMLWriter $objWriter, Gridlines $oGridlines): void
2353    {
2354        // c:spPr
2355        $objWriter->startElement('c:spPr');
2356
2357        // Outline
2358        $this->writeOutline($objWriter, $oGridlines->getOutline());
2359
2360        // ##c:spPr
2361        $objWriter->endElement();
2362    }
2363}